package com.xxxx.comment.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xxxx.comment.constants.RedisConstants;
import com.xxxx.comment.constants.httpcode.HttpConstants;
import com.xxxx.comment.entity.Blog;
import com.xxxx.comment.entity.Follow;
import com.xxxx.comment.entity.User;
import com.xxxx.comment.entity.dto.Result;
import com.xxxx.comment.entity.dto.UserDTO;
import com.xxxx.comment.entity.vo.LikeVO;
import com.xxxx.comment.entity.vo.ScrollResult;
import com.xxxx.comment.mapper.BlogMapper;
import com.xxxx.comment.mapper.FollowMapper;
import com.xxxx.comment.service.IBlogService;
import com.xxxx.comment.service.IFollowService;
import com.xxxx.comment.service.IUserService;
import com.xxxx.comment.utils.SystemConstants;
import com.xxxx.comment.utils.UserHolder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
@Slf4j
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Resource
    private IUserService userService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private BlogMapper blogMapper;

    @Resource
    private IFollowService followService;

    @Resource
    private FollowMapper followMapper;

    @Override
    public Result queryHotBlog(Integer pageNum) {
        // 根据用户查询
        PageHelper.startPage(pageNum, SystemConstants.MAX_PAGE_SIZE);
        List<Blog> blogs = blogMapper.queryHotBlogs();
        Long userId = UserHolder.getUser().getId();
        for (Blog blog : blogs) {
            User user = userService.getById(blog.getUserId());
            Boolean isLike = stringRedisTemplate.opsForZSet()
                    .score(RedisConstants.BLOG_LIKED_KEY + blog.getId(), userId.toString()) != null;
            blog.setIsLike(isLike);
            blog.setName(user.getNickName());
            blog.setIcon(user.getIcon());
        }
        PageInfo<Blog> pageInfo = new PageInfo<>(blogs);

        return Result.ok(pageInfo);
    }

    @Transactional
    @Override
    public Result likeBlog(Long blogId) {
        Long userId = UserHolder.getUser().getId();
        // 判断当前用户是否点赞过，通过Redis的key进行查询，如果存在就说明点过赞了，不存在说明没点过赞
        String key = RedisConstants.BLOG_LIKED_KEY + blogId;
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
        // 如果score != null，说明Sorted中存在该键，说明点赞过
        boolean isLiked = score != null;
        boolean isSuccess = blogMapper.likedBlog(blogId, isLiked);

        LikeVO likeVO = new LikeVO();
        likeVO.setIsLike(Boolean.FALSE.equals(isLiked));
        if (isSuccess) {
            if (Boolean.TRUE.equals(isLiked)) {
                stringRedisTemplate.opsForZSet().remove(key, userId.toString());
                likeVO.setResult(HttpConstants.NOT_LIKED);
            } else {
                stringRedisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis());
                likeVO.setResult(HttpConstants.LIKED);
            }
            Blog blog = getById(blogId);
            likeVO.setLiked(blog.getLiked());
            return Result.ok(likeVO);
        }

        return Result.fail(HttpConstants.SYSTEM_ERROR);
    }

    @Override
    public Result queryBlogById(Long id) {
        Blog blog = blogMapper.queryBlogById(id);
        User user = userService.getById(blog.getUserId());
        blog.setName(user.getNickName());
        blog.setIcon(user.getIcon());
        return Result.ok(blog);
    }

    @Override
    public Result queryBlogLikes(Long blogId) {
        String key = RedisConstants.BLOG_LIKED_KEY + blogId;
        // 1、查询最早点赞的5个用户
        Set<String> likeSet = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        if (likeSet == null || likeSet.isEmpty())
            return Result.ok(Collections.emptyList());
        // 2、根据用户ID补充相应的用户信息并返回
        List<UserDTO> users = likeSet.stream()
                .map(userId -> {
                    User user = userService.getById(userId);
                    return new UserDTO(user);
                })
                .collect(Collectors.toList());

        return Result.ok(users);
    }

    @Override
    public Result queryMyBlog(Long userId) {
        return Result.ok(blogMapper.queryMyBlogs(userId));
    }

    @Override
    public Result saveBlog(Blog blog) {
        if (blog.getShopId() == null)
            return Result.fail("请选择关联的店铺！");
        // 获取当前登录用户
        UserDTO user = UserHolder.getUser();
        blog.setUserId(user.getId());
        blog.setComments(0);
        // 保存探店博文
        if (!save(blog))
            return Result.fail(HttpConstants.SYSTEM_ERROR);
        // 查询发布该笔记的作者的所有粉丝信息
        List<Follow> follows = followMapper.getByFollowUserId(blog.getUserId());
        // 将博文推送给所有粉丝
        for (Follow follow : follows) {
            Long userId = follow.getUserId();
            String pushKey = RedisConstants.FEED_KEY + userId;
            stringRedisTemplate.opsForZSet().add(pushKey, blog.getId().toString(), System.currentTimeMillis());
        }
        // 返回文章ID给发送笔记的用户
        return Result.ok(blog.getId());
    }

    @Override
    public Result queryBlogOfFollowf(Long max, Integer offset) {
        Long userId = UserHolder.getUser().getId();
        // 1、查询收件箱 ZREVRANGEBYSCORE key Max Min LIMIT offset count
        // 从key的值在[min, max]进行分页(pageNum: offset / count, count: pageSize)查询
        String key = RedisConstants.FEED_KEY + userId;
        Set<ZSetOperations.TypedTuple<String>> tupleSet = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(key, 0, max, offset, 2);
        if (tupleSet == null || tupleSet.isEmpty())
            return Result.ok(Collections.emptyList());
        // 2、解析数据：blogId, minTime(时间戳), offset
        List<Long> ids = new ArrayList<>(tupleSet.size());
        long minTime = 0;
        int os = 1; // 页数
        for (ZSetOperations.TypedTuple<String> tuple : tupleSet) {
            // 获取ID
            ids.add(Long.valueOf(Objects.requireNonNull(tuple.getValue())));
            long time = Objects.requireNonNull(tuple.getScore()).longValue();
            if (time == minTime)
                os++;
            else {
                minTime = time;
                os = 1;
            }
        }
        os = minTime == max ? os : os + offset;
        // 3、根据ID查询blog
        List<Blog> blogs = query().in("id", ids).last("ORDER BY id DESC").list();

        int cnt = 0;
        // 4、封装返回
        for (Blog blog : blogs) {
            // 5.1.查询blog有关的用户
            // queryBlogUser(blog);
            // 5.2.查询blog是否被点赞
            User user = userService.getById(blog.getUserId());
            Boolean isLike = stringRedisTemplate.opsForZSet()
                    .score(RedisConstants.BLOG_LIKED_KEY + blog.getId(), userId.toString()) != null;
            blog.setIsLike(isLike);
            blog.setName(user.getNickName());
            blog.setIcon(user.getIcon());
            cnt++;
        }
        // 6.封装并返回
        ScrollResult r = new ScrollResult();
        r.setList(blogs);
        r.setOffset(os);
        r.setMinTime(minTime);

        return Result.ok(r);
    }

}
